#!/usr/bin/env python
# -*- coding: utf-8 -*-

from googlevoice import *
from googlevoice.util import ParsingError, JSONError
from fytion import *
import sys
import BeautifulSoup
from time import sleep
import re

from config import config
from getpass import getpass

last_send_to=None
voice = None
fetion = None

def extractsms(htmlsms) :
    """
    extractsms  --  extract SMS messages from BeautifulSoup tree of Google Voice SMS HTML.

    Output is a list of dictionaries, one per message.
    """
    msgitems = []                                       # accum message items here
    #   Extract all conversations by searching for a DIV with an ID at top level.
    tree = BeautifulSoup.BeautifulSoup(htmlsms, convertEntities=BeautifulSoup.BeautifulSoup.HTML_ENTITIES)         # parse HTML into tree
    conversations = tree.findAll("div",attrs={"id" : True},recursive=False)
    for conversation in conversations :
        #   For each conversation, extract each row, which is one SMS message.
        rows = conversation.findAll(attrs={"class" : "gc-message-sms-row"})
        for row in rows :                               # for all rows
            #   For each row, which is one message, extract all the fields.
            msgitem = {"id" : conversation["id"]}       # tag this message with conversation ID
            spans = row.findAll("span",attrs={"class" : True}, recursive=False)
            for span in spans :                         # for all spans in row
                cl = span["class"].replace('gc-message-sms-', '')
                msgitem[cl] = (" ".join(span.findAll(text=True))).strip()   # put text in dict
            msgitems.append(msgitem)                    # add msg dictionary to list
    return msgitems

def send_sms(text, send_to):
    r = fetion.send_msg(text, phone=send_to, uri=send_to, fid=send_to)
    if not r:
        print("Fetion sent message to %s: %s" % (send_to, text))
    elif r.args[0] < 0:
        # cannot find contact
        if send_to and re.match(r"(^\d{10}$)|(^\+1\d{10}$)", send_to):
            voice.send_sms(send_to, text)
            print("GVoice sent message to %s: %s" % (send_to, text))
        else:
            last_send_to = None
            voice.send_sms(config.gv_phone, "Cannot send to %s" % (send_to))

def process_cmd(cmd):
    global last_send_to, voice, fetion
    
    if cmd.lower().startswith("msg "):
        try:
            send_to = re.search(r"msg (\d+) ", cmd, re.I).group(1)
            text = re.search(r"msg \d+ (.*)", cmd, re.I).group(1)
        except:
            return
        last_send_to = send_to
        send_sms(text, send_to)
    elif cmd.lower().startswith("call "):
        try:
            call_to = re.search(r"call (\+?\d{10,13})", cmd, re.I).group(1)
        except:
            return
        voice.call(call_to, config.gv_phone)
        print "Calling %s" % call_to
    elif cmd.lower().strip() == "conn":
        if not fetion._state:
            fetion.login('100')
            fetion.background_tick()
            voice.send_sms(config.gv_phone, "Fetion reconnected")
    else:
        send_sms(cmd, last_send_to)

def fetion_exited(fetion, e):
    if fetion.stop or not e or e.args[0] == -300:
        print("Fetion deregistered. Will not reconnect.")
        voice.send_sms(config.gv_phone, "Fetion deregistered")
    else:
        print("Reconnect to fetion...")
        fetion.login('100')
        fetion.background_tick()
        print("Reconnected")

def fetion_getmsg(fetion, msg):
    global last_send_to, voice
    
    if msg['from'] == 'sip:10000@fetion.com.cn;p=100': return
    c = fetion.get_contact(uri=msg['from'])
    f = ''
    n = msg['from']
    if c:
        if c['phone']: f = c['phone']
        if not f and c['fetion_id']: f = c['fetion_id']
        n = c['name']
    if not f:
        f = msg['from']
    
    print("Fetion got message from %s(%s): %s" % (n, f, msg['body']))
    if msg['body'].strip() == 'Call':
        #voice.call("13233387261", config.gv_phone)
        voice.call("+86%s" % f, config.gv_phone)
        fetion.send_sms('Sure!', phone=f)
    else:
        voice.send_sms(config.gv_phone, "%s:%s" % (n, msg['body']))
    if not last_send_to:
        last_send_to = f

def main():
    global voice, fetion

    if config.gv_phone is None:
        gv_phone = str(input("Google Voice cellphone number:"))
        config.set('gv_phone', gv_phone)
    
    gv_email = config.gv_email
    if gv_email is None:
        gv_email = raw_input("Google account:")
    gv_pwd = config.gv_password
    if gv_pwd is None:
        gv_pwd = getpass()

    voice = Voice()
    voice.login(email=gv_email, passwd=gv_pwd)
    print "Gvoice online"

    f_user = config.f_user
    if f_user is None:
        f_user = raw_input("Fetion account:")
    f_pwd = config.f_password
    if f_pwd is None:
        f_pwd = getpass()
    
    fetion = fytion()
    fetion.init(phoneNo = f_user, password = f_pwd)
    fetion.login('100')
    print "Fetion online"
    fetion.background_tick()
    fetion.exitCallback = fetion_exited
    fetion.msgCallback = fetion_getmsg
    
    try:
        while True:
            try:
                f = voice.sms()
            except (ParsingError, JSONError):
                print("Reconnect to gvoice...")
                voice.login(email=gv_email, passwd=gv_pwd)
                print("Reconnected")
                continue
            if f.unreadCounts["unread"]:
                msgs = extractsms(voice.sms.html)
                for msg in f.messages:
                    forward = False
                    if msg.phoneNumber[-len(config.gv_phone) : ] != config.gv_phone:
                        forward = True
                    if msg.isRead:
                        continue
                    latest_msg = None
                    for m in msgs:
                        if m['id'] == msg.id:
                            latest_msg = m
                        elif latest_msg:
                            break
                    print("Gvoice got msg: %s" % latest_msg['text'])
                    if forward:
                        voice.send_sms(config.gv_phone, "%s:%s" % (msg.phoneNumber, latest_msg['text'].encode('ascii', 'ignore')))
                    else:
                        process_cmd(latest_msg['text'])
                    msg.mark()
            sleep(5)
    except KeyboardInterrupt:
        print "Exiting fetion"
        fetion.exit()

if __name__ == "__main__":
    sys.exit(main())
